#include "String.h"

#include <cstring> //for counting the number of characters in utf8 string
#include <cstdint>
#include <utility>
#include <string> //for std::char_traits<char16_t>::length
#include "Math/Math.h"

CString::CString()
{
    Data.Add(U'\0');
}

CString::CString(const char8_t *InUtf8String)
{   
    Data =ToUtf32(InUtf8String);
}

CString::CString(const char16_t *InUtf16String)
{
    Data = ToUtf32(InUtf16String);
}

CString::CString(const char32_t *InUtf32String)
{
    int32_t Length = 0;
    while (InUtf32String[Length]!=U'\0')
    {
        ++Length;
    }
    Data= TVector<char32_t>(InUtf32String, Length + 1);
}

CString::CString(char32_t InUtf32Char)
{
    Data.Add(InUtf32Char);
    Data.Add(U'\0');
}

CString::CString(int8_t InNum)
{
    char32_t Buffer[4];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');
}

CString::CString(uint8_t InNum)
{
    char32_t Buffer[3];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');
}

CString::CString(int16_t InNum)
{
    char32_t Buffer[6];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');
}

CString::CString(uint16_t InNum)
{
    char32_t Buffer[5];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');
}

CString::CString(int32_t InNum)
{
    char32_t Buffer[11];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');
}

CString::CString(uint32_t InNum)
{
    char32_t Buffer[10];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');
}

CString::CString(int64_t InNum)
{
    char32_t Buffer[32];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);
    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');
}

CString::CString(uint64_t InNum)
{
    char32_t Buffer[32];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } 
    while (InNum > 0);
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');

}

CString::CString(double InNum, int32_t Precision)
{
    char32_t Buffer[32];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    int64_t IntPart = (int64_t)InNum;
    do
    {
        Buffer[Length++] = U'0' + IntPart % 10;
        IntPart /= 10;
    } while (IntPart > 0);
    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }
    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'.');
    double DecimalPart = InNum - (int64_t)InNum;
    for (int32_t i = 0; i < Precision; ++i)
    {
        DecimalPart *= 10;
        Data.Add(U'0' + (int64_t)DecimalPart);
        DecimalPart -= (int64_t)DecimalPart;
    }
    Data.Add(U'\0');

}

CString::CString(bool InValue)
{
    if (InValue)
    {
        Data.Reserve(5);
        Data.Add(U't');
        Data.Add(U'r');
        Data.Add(U'u');
        Data.Add(U'e');
    }
    else
    {
        Data.Reserve(6);
        Data.Add(U'f');
        Data.Add(U'a');
        Data.Add(U'l');
        Data.Add(U's');
        Data.Add(U'e');
    }
    Data.Add(U'\0');
}

CString::CString(const CString &Other)
{
    Data = Other.Data;
}

CString::CString(const CString &&Other)
{
    Data = std::move(Other.Data);
}

CString &CString::operator=(const CString &Other)
{
    if(this==&Other)
    {
        return *this;
    }

    Data = Other.Data;
    return *this;
}

CString &CString::operator=(const CString &&Other)
{
    if(this==&Other)
    {
        return *this;
    }

    Data = std::move(Other.Data);
    return *this;
}

int64_t CString::ToInt64() const
{
    int64_t Result = 0;
    int32_t Length = Data.Num() - 1;
    int32_t StartIndex = 0;
    bool IsNegative = false;
    if (Data[0] == U'-')
    {
        IsNegative = true;
        ++StartIndex;
    }
    for (int32_t i = StartIndex; i < Length; ++i)
    {
        Result *= 10;
        Result += Data[i] - U'0';
    }
    if (IsNegative)
    {
        Result = -Result;
    }
    return Result;
}

uint64_t CString::ToUInt64() const
{
    uint64_t Result = 0;
    int32_t Length = Data.Num() - 1;
    for (int32_t i = 0; i < Length; ++i)
    {
        Result *= 10;
        Result += Data[i] - U'0';
    }
    return Result;
}

bool CString::IsDigit() const
{
    int32_t Length = Data.Num() - 1;
    for (int32_t i = 0; i < Length; ++i)
    {
        if (Data[i] < U'0' || Data[i] > U'9')
        {
            return false;
        }
    }
    return true;
}

bool CString::IsInt() const
{
    int32_t Length = Data.Num() - 1;
    int32_t StartIndex = 0;
    if (Data[0] == U'-')
    {
        StartIndex = 1;
    }
    for (int32_t i = StartIndex; i < Length; ++i)
    {
        if (Data[i] < U'0' || Data[i] > U'9')
        {
            return false;
        }
    }
    return true;
}

bool CString::IsFloat() const
{
    int32_t Length = Data.Num() - 1;
    int32_t StartIndex = 0;
    if (Data[0] == U'-')
    {
        StartIndex = 1;
    }
    bool HasDot = false;
    for (int32_t i = StartIndex; i < Length; ++i)
    {
        if (Data[i] == U'.')
        {
            if (HasDot)
            {
                return false;
            }
            HasDot = true;
        }
        else if (Data[i] < U'0' || Data[i] > U'9')
        {
            return false;
        }
    }
    return true;
}

uint64_t CString::HexToUInt64() const
{
    //ignore 0x if present
    int32_t StartIndex = 0;
    if (Data[0] == U'0' && Data[1] == U'x')
    {
        StartIndex = 2;
    }

    uint64_t Result = 0;
    int32_t Length = Data.Num() - 1;

    for(uint32_t CharIndex=StartIndex; CharIndex<Length; CharIndex++)
    {
        Result <<= 4;
        if(Data[CharIndex] >= U'0' && Data[CharIndex] <= U'9')
        {
            Result += Data[CharIndex] - U'0';
        }
        else if(Data[CharIndex] >= U'a' && Data[CharIndex] <= U'f')
        {
            Result += Data[CharIndex] - U'a' + 10;
        }
        else if(Data[CharIndex] >= U'A' && Data[CharIndex] <= U'F')
        {
            Result += Data[CharIndex] - U'A' + 10;
        }
        else
        {
            break;
        }
    }
    return Result;
}

bool CString::IsHex() const
{
    //ignore 0x if present
    int32_t StartIndex = 0;
    if (Data[0] == U'0' && Data[1] == U'x')
    {
        StartIndex = 2;
    }

    int32_t Length = Data.Num() - 1;

    for(uint32_t CharIndex=StartIndex; CharIndex<Length; CharIndex++)
    {
        if(!((Data[CharIndex] >= U'0' && Data[CharIndex] <= U'9') ||
            (Data[CharIndex] >= U'a' && Data[CharIndex] <= U'f') ||
            (Data[CharIndex] >= U'A' && Data[CharIndex] <= U'F')))
        {
            return false;
        }
    }
    return true;
}

CString CString::CreateHex(uint64_t InNum, bool bWithPrefix)
{
    static char32_t Map[16]=
    {
        U'0', U'1', U'2', U'3',
        U'4', U'5', U'6', U'7',
        U'8', U'9', U'A', U'B',
        U'C', U'D', U'E', U'F'
    };
    
    TVector<char32_t> Buffer;
    Buffer.Reserve(bWithPrefix? 2+16 : 16);
    if(bWithPrefix)
    {
        Buffer.Add(U'0');
        Buffer.Add(U'x');
    }

    for(int32_t Bit=60; Bit>=0; Bit-=4)
    {
        uint8_t CharValue= (InNum>>Bit) & 0xF;
        Buffer.Add(Map[CharValue]);
    }
    Buffer.Add(U'\0');

    return CString(Buffer.Data());
}

CString CString::CreateBase10(int64_t InNum, TOptional<int64_t> MinWidth)
{
    char32_t Buffer[32];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InNum < 0)
    {
        IsNegative = true;
        InNum = -InNum;
    }
    do
    {
        Buffer[Length++] = U'0' + InNum % 10;
        InNum /= 10;
    } while (InNum > 0);


    if(MinWidth.HasValue())
    {
        int64_t Width = MinWidth.GetValue();
        assert(Width>=0);
        while(Length < Width)
        {
            Buffer[Length++] = U'0';
        }
    }

    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }

    TVector<char32_t> Data;
    Data.Reserve(Length+1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }
    Data.Add(U'\0');

    CString Result;
    Result.Data=std::move(Data);

    return Result;
}

double CString::ToDouble() const
{
    double Result = 0;
    int32_t Length = Data.Num() - 1;
    int32_t StartIndex = 0;
    bool IsNegative = false;
    if (Data[0] == U'-')
    {
        IsNegative = true;
        ++StartIndex;
    }
    int32_t DotIndex = -1;
    for (int32_t i = StartIndex; i < Length; ++i)
    {
        if (Data[i] == U'.')
        {
            DotIndex = i;
            break;
        }
        Result *= 10;
        Result += Data[i] - U'0';
    }
    if (DotIndex != -1)
    {
        double DecimalPart = 0;
        for (int32_t i = DotIndex + 1; i < Length; ++i)
        {
            DecimalPart *= 10;
            DecimalPart += Data[i] - U'0';
        }
        while (DecimalPart >= 1)
        {
            DecimalPart *= 0.1;
        }
        Result += DecimalPart;
    }
    if (IsNegative)
    {
        Result = -Result;
    }
    return Result;
}

bool CString::ToBool() const
{
    if (Data[0] == U't')
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool CString::operator==(const CString &Other) const
{
    if(Data.Num() != Other.Data.Num())
    {
        return false;
    }
    
    //compare the memory
    return memcmp(Data.Data(), Other.Data.Data(), Data.Num() * sizeof(char32_t)) == 0;

}

bool CString::operator<(const CString &Other) const
{
    return Data < Other.Data;
}

uint64_t CString::GetHashCode() const
{
    uint64_t Hash = 0;
    for (uint64_t i = 0; i < Data.Num() - 1; ++i)
    {
        Hash = Hash * 31 + Data[i];
    }
    return Hash;
}

uint64_t CString::GetLength() const
{
    return Data.Num() - 1;
}

char32_t CString::GetCharAt(uint64_t Index) const
{
    assert(Index < GetLength());
    return Data[Index];
}

void CString::Reserve(uint64_t InCharCount)
{
    Data.Reserve(InCharCount + 1);
}

void CString::Resize(uint64_t InCharCount)
{
    Data.Resize(InCharCount + 1);
    Data.Last() = U'\0';
}

const char32_t *CString::GetData() const
{
    return Data.Data();
}

TVector<char32_t> CString::GetUnicodes() const
{
    return Data;
}

CString CString::operator+(const CString &Other) const
{
    CString Result;
    
    Result.Reserve(GetLength() + Other.GetLength());
    Result.Data.Clear(false);    

    for(uint64_t i = 0; i < Data.Num() - 1; ++i)
    {
        Result.Data.Add(Data[i]);
    }
 
    for(uint64_t i = 0; i < Other.Data.Num(); ++i)
    {
        Result.Data.Add(Other.Data[i]);
    }

    return Result;
}

// CString CString::operator+(const char32_t Other []) const
// {
//     CString Result = *this + CString(Other);

//     return Result;
// }

// CString CString::operator+(char32_t Other) const
// {
//     CString Result;
//     Result.Reserve(GetLength() + 1);

//     for(uint64_t i = 0; i < Data.Num() - 1; ++i)
//     {
//         Result.Data.Add(Data[i]);
//     }
 
//     Result.Data.Add(Other);
//     Result.Data.Add(U'\0');

//     return Result;
// }

CString &CString::operator+=(const CString &Other)
{
    uint64_t OldLength = GetLength();
    Data.Resize(Data.Num() + Other.GetLength(),false);
    memcpy(Data.GetDataPtr(OldLength), Other.Data.GetDataPtr(0), Other.Data.Num() * sizeof(char32_t));

    return *this;
}

CString &CString::operator+=(char32_t Other)
{
    Reserve(GetLength() + 1);
    Data.Reserve(Data.Num() + 1);

    Data[Data.Num() - 1] = Other;
    Data.Add(U'\0');

    return *this;
}

const char32_t& CString::operator[](uint64_t Index) const
{
    return Data[Index]; 
}

char32_t& CString::operator[](uint64_t Index)
{
    return Data[Index];
}

bool CString::IsEmpty() const
{
    return Data.Num() == 1;
}

void CString::Clear()
{
    Data.Clear();
    Data.Add(U'\0');
}

bool CString::StartsWith(const CString &Other) const
{
    if (Other.GetLength() > GetLength())
    {
        return false;
    }
    for (uint64_t i = 0; i < Other.GetLength(); ++i)
    {
        if (Data[i] != Other.Data[i])
        {
            return false;
        }
    }
    return true;
}

bool CString::EndsWith(const CString &Other) const
{
    if (Other.GetLength() > GetLength())
    {
        return false;
    }
    for (uint64_t i = 0; i < Other.GetLength(); ++i)
    {
        if (Data[GetLength() - i - 1] != Other.Data[Other.GetLength() - i - 1])
        {
            return false;
        }
    }
    return true;
}

CString CString::Join(const std::list<CString> &Strings)
{
    CString Result;
    uint64_t TotalLength = 0;
    for (auto &String : Strings)
    {
        TotalLength += String.GetLength();
    }
    Result.Resize(TotalLength);

    uint64_t StartIndex = 0;
    for (auto &String : Strings)
    {
        memcpy(Result.Data.Data() + StartIndex, String.Data.Data(), String.GetLength() * sizeof(char32_t));
        StartIndex += String.GetLength();
    }

    Result.Data[TotalLength] = U'\0';


    return Result;
}

CString CString::SubString(uint64_t StartIndex, uint64_t InLength) const
{
    uint64_t Length =  CMath::Min(InLength, GetLength() - StartIndex);
    CString Result;
    Result.Data.Resize(Length + 1);

    memcpy(Result.Data.Data(), Data.Data() + StartIndex, Length * sizeof(char32_t));

    Result.Data[Length] = U'\0';

    return Result;
}

CString CString::SubString(uint64_t StartIndex) const
{
    return SubString(StartIndex, GetLength() - StartIndex);
}

CString CString::Left(uint64_t Length) const
{
    return SubString(0, Length);
}

CString CString::Right(uint64_t Length) const
{
    return SubString(GetLength() - Length, Length);
}

CString CString::Insert(uint64_t Index, const CString &Other) const
{
    if(Index==GetLength()+1)
    {
        //append at the end
        return *this + Other;
    }

    assert(Index < GetLength());
    
    CString NewString;
    NewString.Resize(GetLength()+Other.GetLength());


    memcpy(NewString.Data.GetDataPtr(0)
        ,Data.GetDataPtr(0)
        ,Index * sizeof(char32_t)
    );

    memcpy(NewString.Data.GetDataPtr(Index)
        ,Other.Data.GetDataPtr(0)
        ,Other.GetLength() * sizeof(char32_t)
    );

    memcpy(NewString.Data.GetDataPtr(Index+Other.GetLength())
        ,Data.GetDataPtr(Index)
        ,(GetLength()-Index) * sizeof(char32_t)
    );

    return NewString;
}

int64_t CString::Find(const CString &Other, uint64_t StartIndex) const
{
    if (Other.GetLength() > GetLength())
    {
        return -1;
    }
    for (uint64_t i = StartIndex; i < GetLength() - Other.GetLength() + 1; ++i)
    {
        bool IsMatch = true;
        for (uint64_t j = 0; j < Other.GetLength(); ++j)
        {
            if (Data[i + j] != Other.Data[j])
            {
                IsMatch = false;
                break;
            }
        }
        if (IsMatch)
        {
            return i;
        }
    }
    return -1;
}

int64_t CString::FindLast(const CString &Other) const
{
    if (Other.GetLength() > GetLength())
    {
        return -1;
    }
    for (int64_t i = (GetLength() - Other.GetLength()); i >= 0; --i)
    {
        bool IsMatch = true;
        for (int64_t j = 0; j < Other.GetLength(); ++j)
        {
            if (Data[i + j] != Other.Data[j])
            {
                IsMatch = false;
                break;
            }
        }
        if (IsMatch)
        {
            return i;
        }
    }
    return -1;
}

int64_t CString::Find(char32_t Other, uint64_t StartIndex) const
{
    for (int64_t i = StartIndex; i < GetLength(); ++i)
    {
        if (Data[i] == Other)
        {
            return i;
        }
    }
    return -1;
}

int64_t CString::FindLast(char32_t Other) const
{
    for (int64_t i = (GetLength() - 1); i >= 0; --i)
    {
        if (Data[i] == Other)
        {
            return i;
        }
    }
    return -1;
}

int64_t CString::Count(const CString & Other) const
{
    int64_t Result = 0;
    int64_t Index = 0;
    while (true)
    {
        Index = Find(Other, Index);
        if (Index == -1)
        {
            break;
        }
        else
        {
            ++Result;
            Index += Other.GetLength();
        }
    }
    return Result;
}

bool CString::ContainsAnyOf(const CString &Other) const
{
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        for (uint64_t j = 0; j < Other.GetLength(); ++j)
        {
            if (Data[i] == Other.Data[j])
            {
                return true;
            }
        }
    }
    return false;
}

bool CString::Contains(const CString &Other) const
{
    return Find(Other) != -1;
}

CString CString::ToLower() const
{
    CString Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] >= U'A' && Data[i] <= U'Z')
        {
            Result.Data.Add(Data[i] + U'a' - U'A');
        }
        else
        {
            Result.Data.Add(Data[i]);
        }
    }
    Result.Data.Add(U'\0');
    return Result;
}

CString CString::ToUpper() const
{
    CString Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] >= U'a' && Data[i] <= U'z')
        {
            Result.Data.Add(Data[i] + U'A' - U'a');
        }
        else
        {
            Result.Data.Add(Data[i]);
        }
    }
    Result.Data.Add(U'\0');
    return Result;
}

TVector<CString> CString::Split(const CString &Delimiter) const
{
    TVector<CString> Result;
    uint64_t StartIndex = 0;
    while (true)
    {
        uint64_t Index = Find(Delimiter, StartIndex);
        if (Index == -1)
        {
            Result.Add(SubString(StartIndex));
            break;
        }
        else
        {
            //check substring is not empty
            if(Index - StartIndex > 0)
            {
                Result.Add(SubString(StartIndex, Index - StartIndex));
            }
            StartIndex = Index + Delimiter.GetLength();
        }
    }
    return Result;
}

TVector<CString> CString::Split(char32_t Delimiter) const
{
    //1.find all the delimiters and store the indexes into a linked list
    std::list<uint64_t> DelimiterIndexes;
    int64_t Count=0;
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] == Delimiter)
        {
            Count++;
            DelimiterIndexes.push_back(i);
        }
    }

    TVector<CString> Result;
    Result.Reserve(Count + 1);

    if(Count == 0)
    {
        Result.Add(*this);
        return Result;
    }

    //2. iterate over the linked list and create substrings
    uint64_t StartIndex = 0;
    for(auto Index : DelimiterIndexes)
    {
        Result.Add(SubString(StartIndex, Index - StartIndex));
        StartIndex = Index + 1;
    }
    if(StartIndex < GetLength())
    {
        Result.Add(SubString(StartIndex));
    }

    return Result;
}

TVector<CString> CString::SplitByMultiple(const TVector<CString> &Delimiters) const
{
    TVector<CString> Result;

    //iterate over all characters in the string
    uint64_t StartIndex = 0;
    uint64_t CharIndex = 0;
    while (CharIndex < GetLength())
    {
        if(Delimiters.Contains(Data[CharIndex]))
        {
            Result.Add(SubString(StartIndex, CharIndex - StartIndex));
            StartIndex = CharIndex + 1;
        }
        
        CharIndex++;
    }

    //add the last substring
    Result.Add(SubString(StartIndex));

    return Result;
}

CString CString::Trim() const
{
    uint64_t StartIndex = 0;
    while (StartIndex < GetLength() && Data[StartIndex] == U' ')
    {
        ++StartIndex;
    }
    uint64_t EndIndex = GetLength() - 1;
    while (EndIndex >= 0 && Data[EndIndex] == U' ')
    {
        --EndIndex;
    }
    return SubString(StartIndex, EndIndex - StartIndex + 1);
}

CString CString::Replace(const CString &From, const CString &To) const
{
    CString Result;
    uint64_t StartIndex = 0;
    while (true)
    {
        uint64_t Index = Find(From, StartIndex);
        if (Index == -1)
        {
            Result += SubString(StartIndex);
            break;
        }
        else
        {
            Result += SubString(StartIndex, Index - StartIndex);
            Result += To;
            StartIndex = Index + From.GetLength();
        }
    }
    return Result;
}

CString CString::Replace(char32_t From, char32_t To) const
{
    CString Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] == From)
        {
            Result.Data.Add(To);
        }
        else
        {
            Result.Data.Add(Data[i]);
        }
    }
    Result.Data.Add(U'\0');
    return Result;
}

CString CString::Remove(char32_t Char) const
{
    CString Result;
    Result.Reserve(GetLength());
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] != Char)
        {
            Result.Data.Add(Data[i]);
        }
    }
    Result.Data.Add(U'\0');
    return Result;
}

CString CString::RemoveAt(uint64_t Index, uint32_t Length) const
{
    CString Result;
    Result.Reserve(GetLength());
    Result.Data.Clear(); 

    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (i < Index || i >= Index+Length)
        {
            Result.Data.Add(Data[i]);
        }
    }
    Result.Data.Add(U'\0');
    return Result;
}

CString CString::Remove(const CString &Str) const
{
    CString Result;
    uint64_t StartIndex = 0;
    while (true)
    {
        uint64_t Index = Find(Str, StartIndex);
        if (Index == -1)
        {
            Result += SubString(StartIndex);
            break;
        }
        else
        {
            Result += SubString(StartIndex, Index - StartIndex);
            StartIndex = Index + Str.GetLength();
        }
    }
    return Result;
}

CString CString::Repeat(uint64_t Count) const
{
    if(Count == 0)
    {
        return CString();
    }
    
    if(Count == 1)
    {
        return *this;
    }


    int64_t Length=GetLength();
    CString Result;
    Result.Data.Resize(Length * Count + 1);
    for (uint64_t i = 0; i < Count; ++i)
    {
        memcpy(Result.Data.GetDataPtr(i * Length)
            ,Data.GetDataPtr(0)
            ,Length * sizeof(char32_t)
        );
    }
    Result.Data[Length * Count] = U'\0';

    return Result;
}

TVector<char32_t> CString::ToUtf32(const char8_t *InUtf8String)
{
    TVector<char32_t> Result;
    uint64_t Length = std::strlen(reinterpret_cast<const char *>(InUtf8String));
    Result.Reserve(Length);
    for (uint64_t i = 0; i < Length;)
    {
        char32_t Char = 0;
        if ((InUtf8String[i] & 0x80) == 0)
        {
            Char = InUtf8String[i];
            ++i;
        }
        else if ((InUtf8String[i] & 0xE0) == 0xC0)
        {
            Char = (InUtf8String[i] & 0x1F) << 6;
            Char |= (InUtf8String[i + 1] & 0x3F);
            i += 2;
        }
        else if ((InUtf8String[i] & 0xF0) == 0xE0)
        {
            Char = (InUtf8String[i] & 0x0F) << 12;
            Char |= (InUtf8String[i + 1] & 0x3F) << 6;
            Char |= (InUtf8String[i + 2] & 0x3F);
            i += 3;
        }
        else if ((InUtf8String[i] & 0xF8) == 0xF0)
        {
            Char = (InUtf8String[i] & 0x07) << 18;
            Char |= (InUtf8String[i + 1] & 0x3F) << 12;
            Char |= (InUtf8String[i + 2] & 0x3F) << 6;
            Char |= (InUtf8String[i + 3] & 0x3F);
            i += 4;
        }
        else
        {
            assert(false && "Invalid UTF-8 string");
        }
        Result.Add(Char);
    }
    Result.Add(U'\0');
    return Result;

}

TVector<char32_t> CString::ToUtf32(const char16_t *InUtf16String)
{
    TVector<char32_t> Result;
    uint64_t Length = std::char_traits<char16_t>::length(InUtf16String);
    Result.Reserve(Length);
    for (uint64_t i = 0; i < Length;)
    {
        char32_t Char = 0;
        if ((InUtf16String[i] & 0xD800) != 0xD800)
        {
            Char = InUtf16String[i];
            ++i;
        }
        else
        {
            Char = ((InUtf16String[i] & 0x3FF) << 10) | (InUtf16String[i + 1] & 0x3FF);
            Char += 0x10000;
            i += 2;
        }
        Result.Add(Char);
    }
    Result.Add(U'\0');
    return Result;

}

TVector<char8_t> CString::ToUtf8() const
{
    TVector<char8_t> Result;
    Result.Reserve(GetLength() * 4);
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] < 0x80)
        {
            Result.Add(static_cast<char8_t>(Data[i]));
        }
        else if (Data[i] < 0x800)
        {
            Result.Add(static_cast<char8_t>(0xC0 | (Data[i] >> 6)));
            Result.Add(static_cast<char8_t>(0x80 | (Data[i] & 0x3F)));
        }
        else if (Data[i] < 0x10000)
        {
            Result.Add(static_cast<char8_t>(0xE0 | (Data[i] >> 12)));
            Result.Add(static_cast<char8_t>(0x80 | ((Data[i] >> 6) & 0x3F)));
            Result.Add(static_cast<char8_t>(0x80 | (Data[i] & 0x3F)));
        }
        else
        {
            Result.Add(static_cast<char8_t>(0xF0 | (Data[i] >> 18)));
            Result.Add(static_cast<char8_t>(0x80 | ((Data[i] >> 12) & 0x3F)));
            Result.Add(static_cast<char8_t>(0x80 | ((Data[i] >> 6) & 0x3F)));
            Result.Add(static_cast<char8_t>(0x80 | (Data[i] & 0x3F)));
        }
    }
    Result.Add('\0');
    return Result;
}

CString8 CString::ToUtf8String() const
{
    auto Utf8Chars = ToUtf8();
    return CString8(Utf8Chars.Data());
}

TVector<char16_t> CString::ToUtf16() const
{
    TVector<char16_t> Result;
    Result.Reserve(GetLength() * 2);
    for (uint64_t i = 0; i < GetLength(); ++i)
    {
        if (Data[i] < 0x10000)
        {
            Result.Add(static_cast<char16_t>(Data[i]));
        }
        else
        {
            Result.Add(static_cast<char16_t>(0xD800 | ((Data[i] - 0x10000) >> 10)));
            Result.Add(static_cast<char16_t>(0xDC00 | ((Data[i] - 0x10000) & 0x3FF)));
        }
    }
    Result.Add('\0');
    return Result;
}

CString operator+(const char32_t *Left, const CString &Right)
{
    return CString(Left) + Right;
}

CString operator+(char32_t Left, const CString &Right)
{
    CString Result;
    Result.Reserve(Right.GetLength() + 1);
    Result.Data[0] = Left;

    for(uint64_t i = 0; i < Right.GetLength(); ++i)
    {
        Result.Data.Add(Right.Data[i]);
    }
    
    Result.Data.Add('\0');

    return Result;
}

CString WH::ToString(const CString &InValue)
{
    return InValue;
}

#ifdef USING_SOURCE_LOCATION
CString WH::ToString(const std::source_location &InSourceLocation)
{
    return CString((char8_t*)InSourceLocation.file_name()) + U":" + CString(InSourceLocation.line()) + U":" + CString(InSourceLocation.column());
}
#endif

CString WH::ToString(bool InValue)
{
    if (InValue)
    {
        return U"true";
    }
    else
    {
        return U"false";
    }
}

CString WH::ToString(int8_t InValue)
{
    char32_t Buffer[4];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InValue < 0)
    {
        IsNegative = true;
        InValue = -InValue;
    }

    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(uint8_t InValue)
{
    char32_t Buffer[3];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(int16_t InValue)
{
    char32_t Buffer[6];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InValue < 0)
    {
        IsNegative = true;
        InValue = -InValue;
    }

    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(uint16_t InValue)
{
    char32_t Buffer[5];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(int32_t InValue)
{
    char32_t Buffer[12];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InValue < 0)
    {
        IsNegative = true;
        InValue = -InValue;
    }

    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(uint32_t InValue)
{
    char32_t Buffer[10];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(int64_t InValue)
{
    char32_t Buffer[21];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InValue < 0)
    {
        IsNegative = true;
        InValue = -InValue;
    }

    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(uint64_t InValue)
{
    char32_t Buffer[20];
    int32_t Length = 0;
    do
    {
        Buffer[Length++] = U'0' + InValue % 10;
        InValue /= 10;
    } while (InValue > 0);

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(float InValue, int32_t Precision)
{
    char32_t Buffer[64];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InValue < 0)
    {
        IsNegative = true;
        InValue = -InValue;
    }

    int32_t IntegerPart = static_cast<int32_t>(InValue);
    float DecimalPart = InValue - IntegerPart;

    do
    {
        Buffer[Length++] = U'0' + IntegerPart % 10;
        IntegerPart /= 10;
    } while (IntegerPart > 0);

    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'.');

    for (int32_t i = 0; i < Precision; ++i)
    {
        DecimalPart *= 10;
        Data.Add(U'0' + static_cast<int32_t>(DecimalPart));
        DecimalPart -= static_cast<int32_t>(DecimalPart);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}

WHENGINE_API CString WH::ToString(double InValue, int32_t Precision)
{
    char32_t Buffer[64];
    int32_t Length = 0;
    bool IsNegative = false;
    if (InValue < 0)
    {
        IsNegative = true;
        InValue = -InValue;
    }

    int32_t IntegerPart = static_cast<int32_t>(InValue);
    double DecimalPart = InValue - IntegerPart;

    do
    {
        Buffer[Length++] = U'0' + IntegerPart % 10;
        IntegerPart /= 10;
    } while (IntegerPart > 0);

    if (IsNegative)
    {
        Buffer[Length++] = U'-';
    }

    TVector<char32_t> Data;
    Data.Reserve(Length + 1);

    for (int32_t i = 0; i < Length; ++i)
    {
        Data.Add(Buffer[Length - i - 1]);
    }

    Data.Add(U'.');

    for (int32_t i = 0; i < Precision; ++i)
    {
        DecimalPart *= 10;
        Data.Add(U'0' + static_cast<int32_t>(DecimalPart));
        DecimalPart -= static_cast<int32_t>(DecimalPart);
    }

    Data.Add(U'\0');

    return CString(Data.Data());
}
